package com.linxuan.reggie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.linxuan.reggie.common.BaseContext;
import com.linxuan.reggie.common.CustomException;
import com.linxuan.reggie.dao.OrderMapper;
import com.linxuan.reggie.dto.OrdersDto;
import com.linxuan.reggie.entity.*;
import com.linxuan.reggie.service.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 订单业务层，添加上事务注解，开启事务
 */
@Service
@Transactional
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Orders> implements OrderService {

    @Autowired
    private ShoppingCartService shoppingCartService;

    @Autowired
    private UserService userService;

    @Autowired
    private AddressBookService addressBookService;

    @Autowired
    private OrderDetailService orderDetailService;

    /**
     * 向订单表以及订单信息表添加订单信息，逻辑如下：
     * 1. 查询当前用户的购物车数据，如果购物车数据为空，那么抛出异常
     * 2. 查询前端传过来的地址簿ID数据，如果为空，那么返回
     * 3. 查询当前用户登录信息
     * 4. 根据查询出来的信息设置一条订单详情数据并插入数据库中
     * 5. 根据以上查询出来的信息设置一条订单数据并插入数据库中，
     * 设置数据：
     *
     * @param orders 订单信息
     */
    @Override
    public void submit(Orders orders) {
        // 获取到当前登录系统前台的用户ID
        Long userId = BaseContext.getCurrentId();

        // 查询当前用户的购物车数据
        LambdaQueryWrapper<ShoppingCart> shoppingCartLambdaQueryWrapper = new LambdaQueryWrapper<>();
        shoppingCartLambdaQueryWrapper.eq(userId != null, ShoppingCart::getUserId, userId);
        List<ShoppingCart> shoppingCartList = shoppingCartService.list(shoppingCartLambdaQueryWrapper);
        // 如果查询出来的购物车数据为空，那么抛出异常
        if (shoppingCartList == null || shoppingCartList.size() == 0) {
            throw new CustomException("购物车为空，不能下单");
        }

        // 查询前端传过来的地址簿ID数据，也就是该订单的地址簿信息
        Long addressBookId = orders.getAddressBookId();
        AddressBook addressBook = addressBookService.getById(addressBookId);
        // 如果查询出来的地址簿数据为空，那么抛出异常
        if (addressBook == null) {
            throw new CustomException("地址有误，不能下单");
        }

        // 查询登录系统前台用户的信息
        User user = userService.getById(userId);

        // 使用MP自带工具类生成订单号
        long orderId = IdWorker.getId();
        // 设置初始总金额
        AtomicInteger amount = new AtomicInteger(0);

        // 根据查询出来的购物车信息创建与之匹配的订单详情数据并插入数据库oder_detail表中
        // 需要设置的数据有：菜品/套餐名称、订单id、菜品id、套餐id、口味、数量、金额、图片
        List<OrderDetail> orderDetails = shoppingCartList.stream().map((item) -> {
            // 创建与购物车信息相匹配的订单详情数据
            OrderDetail orderDetail = new OrderDetail();
            // 根据购物车中的菜品/套餐名称设置订单详情中的菜品/套餐名称
            orderDetail.setName(item.getName());
            // 设置订单ID
            orderDetail.setOrderId(orderId);
            // 设置菜品ID
            orderDetail.setDishId(item.getDishId());
            // 设置套餐ID
            orderDetail.setSetmealId(item.getSetmealId());
            // 设置口味
            orderDetail.setDishFlavor(item.getDishFlavor());
            // 设置数量
            orderDetail.setNumber(item.getNumber());
            // 设置金额
            orderDetail.setImage(item.getImage());
            // 设置金额
            orderDetail.setAmount(item.getAmount());
            // 设置该购物车的总金额
            // addAndGet将给定的值原子地添加到当前值。 multiply返回BigDecimal，其值是 (this × multiplicand)，
            amount.addAndGet(item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue());
            return orderDetail;
        }).collect(Collectors.toList());


        // 对前端传过来的订单数据的部分内容进行设置，然后向订单表中插入，
        // 前端传过来的数据有：备注信息remark、支付方式payMethod、地址簿ID addressBookId
        // 需要设置的数据有：订单号、订单状态、下单用户id、下单时间、结账时间、实收金额、用户名、手机号、地址、收货人
        // 设置订单号(利用MP工具类生成)
        orders.setNumber(String.valueOf(orderId));
        // 设置订单状态(2待派送)
        orders.setStatus(2);
        // 设置下单用户ID(BaseContext.currentId())
        orders.setUserId(userId);
        // 设置下单时间orderTime(LocalDateTime.now())
        orders.setOrderTime(LocalDateTime.now());
        // 设置结账时间checkoutTime(LocalDateTime.now())
        orders.setCheckoutTime(LocalDateTime.now());
        // 设置实收金额amount(将购物车中金额数据相加起来，前面创建订单详情数据的时候已经计算过了)
        orders.setAmount(new BigDecimal(amount.get()));
        // 设置用户名userName(根据用户ID查询出来的用户信息)
        orders.setUserName(user.getName());
        // 设置手机号phone(根据用户ID查询出来的用户信息)
        orders.setPhone(addressBook.getPhone());
        // 设置地址address(根据地址簿ID查询出来的地址信息，需要组装)
        orders.setAddress((addressBook.getProvinceName() == null ? "" : addressBook.getProvinceName())
                + (addressBook.getCityName() == null ? "" : addressBook.getCityName())
                + (addressBook.getDistrictName() == null ? "" : addressBook.getDistrictName())
                + (addressBook.getDetail() == null ? "" : addressBook.getDetail()));
        // 设置收货人consignee(根据地址簿ID查询出来的收货人)
        orders.setConsignee(addressBook.getConsignee());

        // 向订单表中插入数据
        this.save(orders);
        // 向订单明细表中插入多条数据
        orderDetailService.saveBatch(orderDetails);
        // 清空购物车数据
        shoppingCartService.remove(shoppingCartLambdaQueryWrapper);
    }

    /**
     * 查询订单表以及订单详情表组装成OrdersDto类型返回
     * @param page 分页页数
     * @param pageSize 每页数量
     * @return Page<OrdersDto>
     */
    @Override
    public Page<OrdersDto> getOrdersDtoPage(int page, int pageSize) {

        // 分页查询构造器
        Page<Orders> ordersPage = new Page<>(page, pageSize);
        // 查询条件
        LambdaQueryWrapper<Orders> ordersLambdaQueryWrapper = new LambdaQueryWrapper<>();
        ordersLambdaQueryWrapper.eq(Orders::getUserId, BaseContext.getCurrentId());
        ordersLambdaQueryWrapper.orderByDesc(Orders::getOrderTime);
        // 分页查询，查询后结果都放进了ordersPage里面
        this.page(ordersPage, ordersLambdaQueryWrapper);

        // 将分页查询出来的值出去records记录外都拷贝给ordersDtoPage
        Page<OrdersDto> ordersDtoPage = new Page<>();
        BeanUtils.copyProperties(ordersPage, ordersDtoPage, "records");

        // 根据订单号去查询订单详情并设置到ordersDtoPage的records属性中去
        // 在后端我们无需设置订单的商品总量，前端已经帮我们计算过了
        List<OrdersDto> ordersDtoList = ordersPage.getRecords().stream().map((item) -> {
            OrdersDto ordersDto = new OrdersDto();
            BeanUtils.copyProperties(item, ordersDto);

            // 获取该条订单的订单号，根据订单号去查询订单详情
            String orderId = item.getNumber();
            LambdaQueryWrapper<OrderDetail> orderDetailWrapper = new LambdaQueryWrapper<>();
            orderDetailWrapper.eq(OrderDetail::getOrderId, orderId);
            List<OrderDetail> orderDetailList = orderDetailService.list(orderDetailWrapper);

            // 设置订单详情
            ordersDto.setOrderDetails(orderDetailList);

            return ordersDto;
        }).collect(Collectors.toList());
        ordersDtoPage.setRecords(ordersDtoList);

        return ordersDtoPage;
    }

    /**
     * 再来一单功能
     * 根据订单主键ID查询订单号，根据订单号查询订单详情信息，将这些订单详情信息添加至购物车
     * @param id 订单主键ID
     */
    @Override
    public boolean again(Long id) {

        // 获取当前登录系统前台用户ID
        Long userId = BaseContext.getCurrentId();

        // 判断当前用户购物车是否有数据，如果有，那么报错
        LambdaQueryWrapper<ShoppingCart> shoppingCartLambdaQueryWrapper = new LambdaQueryWrapper<>();
        shoppingCartLambdaQueryWrapper.eq(ShoppingCart::getUserId, userId);
        List<ShoppingCart> shoppingCarts = shoppingCartService.list(shoppingCartLambdaQueryWrapper);
        // 如果查询出来该用户购物车数据大于或者等于1条，那么要提醒用户清空购物车再点击「再来一单」
        if (shoppingCarts.size() >= 1) {
            return false;
        }

        // 获取到对应的订单信息
        Orders orders = this.getById(id);
        // 获取到对应订单号
        String number = orders.getNumber();

        // 根据订单号查询对应的订单详情信息列表
        LambdaQueryWrapper<OrderDetail> orderDetailWrapper = new LambdaQueryWrapper<>();
        orderDetailWrapper.eq(number != null, OrderDetail::getOrderId, number);
        List<OrderDetail> orderDetailList = orderDetailService.list(orderDetailWrapper);

        // 创建对应的购物车信息
        orderDetailList.forEach((item) -> {
            ShoppingCart shoppingCart = new ShoppingCart();
            // 设置该条购物车数据菜品/套餐名称
            shoppingCart.setName(item.getName());
            // 设置该条购物车数据菜品/套餐图片
            shoppingCart.setImage(item.getImage());
            // 设置该条购物车数据用户ID
            shoppingCart.setUserId(userId);
            // 设置该条购物车数据菜品ID
            shoppingCart.setDishId(item.getDishId());
            // 设置该条购物车数据套餐ID
            shoppingCart.setSetmealId(item.getSetmealId());
            // 设置该条购物车数据菜品口味信息
            shoppingCart.setDishFlavor(item.getDishFlavor());
            // 设置该条购物车数据菜品/套餐数量
            shoppingCart.setNumber(item.getNumber());
            // 设置该条购物车数据菜品/套餐金额
            shoppingCart.setAmount(item.getAmount());
            // 设置该条购物车数据创建时间
            shoppingCart.setCreateTime(LocalDateTime.now());

            shoppingCarts.add(shoppingCart);
        });

        shoppingCartService.saveBatch(shoppingCarts);
        return true;
    }
}
